﻿//
// dumpConfig.sqf
// Copyright (c) 2010 Denis Usenko, DenVdmj@gmail.com
// MIT-style license
//

/*
======================================================================================

Dump the game config to the clipboard:

    [config HNDL, bool IncludeInheritedPropertiesFlag] call compile preprocessFileLineNumbers "dumpConfig.sqf"

This example puts the section CfgVehicles on the clipboard:

    [configFile >> "CfgVehicles"] call compile preprocessFileLineNumbers "dumpConfig.sqf"

This example will put the class "RscDisplayArcadeUnit" on the clipboard, all classes will contain all heritable properties, so you'll get a full and self-sufficient class, independent from the other classes.

    [configFile >> "RscDisplayArcadeUnit", true] call compile preprocessFileLineNumbers "dumpConfig.sqf"

More examples:

    [configFile >> "RscTitle", true] call compile preprocessFileLineNumbers "dumpConfig.sqf"
    [configFile >> "RscEdit", true] call compile preprocessFileLineNumbers "dumpConfig.sqf"
    [configFile >> "RscToolbox", true] call compile preprocessFileLineNumbers "dumpConfig.sqf"

For several classes:
    [
        [
            configFile >> "RscTitle",
            configFile >> "RscEdit",
            configFile >> "RscToolbox",
            configFile >> "RscCheckBox"
        ],
        true
    ] call compile preprocessFileLineNumbers "dumpConfig.sqf"

Warning: don't attempt to get a large classes with switched on parameter "IncludeInheritedPropertiesFlag", eg don't do so:

    [configFile, true] call compile preprocessFileLineNumbers "dumpConfig.sqf"

Dump the entire config, it can take over ten seconds:

    [configFile] call compile preprocessFileLineNumbers "dumpConfig.sqf"

======================================================================================

Дамп конфига в буфер обмена:

    [конфиг-хэндл нужного класса, включать наследуемые свойства или нет] call compile preprocessFileLineNumbers "dumpConfig.sqf"

Этот пример поместит в буфер обмена раздел "CfgVehicles"

    [configFile >> "CfgVehicles"] call compile preprocessFileLineNumbers "dumpConfig.sqf"

Этот пример поместит в буфер обмена класс "RscDisplayArcadeUnit", все классы будут содержать все наследуемые свойства, то есть вы получаете полный, независимый от других классов класс.

    [configFile >> "RscDisplayArcadeUnit", true] call compile preprocessFileLineNumbers "dumpConfig.sqf"

Еще примеры:

    [configFile >> "RscTitle", true] call compile preprocessFileLineNumbers "dumpConfig.sqf"
    [configFile >> "RscEdit", true] call compile preprocessFileLineNumbers "dumpConfig.sqf"
    [configFile >> "RscToolbox", true] call compile preprocessFileLineNumbers "dumpConfig.sqf"

Вывод нескольких классов:
    [
        [
            configFile >> "RscTitle",
            configFile >> "RscEdit",
            configFile >> "RscToolbox",
            configFile >> "RscCheckBox"
        ],
        true
    ] call compile preprocessFileLineNumbers "dumpConfig.sqf"

Важно: не пытайтесь вывести большие классы с включенным вторым параметром, например не делайте так:

    [configFile, true] call compile preprocessFileLineNumbers "dumpConfig.sqf"

Дамп всего конфига, это может занять более десятка секунд:

    [configFile] call compile preprocessFileLineNumbers "dumpConfig.sqf"

======================================================================================
*/

#define arg(x)          (_this select (x))
#define argIf(x)        if(count _this > (x))
#define argIfType(x,t)  if(argIf(x)then{typeName arg(x) == (t)}else{false})
#define argSafe(x)      argIf(x)then{arg(x)}
#define argSafeType(x,t)  argIfType(x,t)then{arg(x)}
#define argOr(x,v)      (argSafe(x)else{v})
#define __push(a,v)       (a)set[count(a),(v)]
#define __pushTo(a)       call{(a)set[count(a),_this]}
#define __log2(number)    ((log number)/.3010299956639812)

private [
    "_joinString",
    "_escapeString",
    "_collectInheritedProperties",
    "_dumpConfigTree"
];

_joinString = {
    //
    // Fast string concatenation,
    //
    private ["_list", "_char", "_size", "_subsize", "_oversize", "_j"];

    _list = arg(0);
    _char = arg(1);

    if (count _list < 1) exitwith {""};

    for "" from 1 to ceil(__log2(count _list)) do {
        _size = count _list / 2;
        _subsize = floor _size;
        _oversize = ceil _size;
        _j = 0;
        for "_i" from 0 to _subsize - 1 do {
            _list set [_i, (_list select _j) + _char + (_list select (_j+1))];
            _j = _j + 2;
        };
        if (_subsize != _oversize) then { // to add a tail
            _list set [_j/2, _list select _j];
        };
        _list resize _oversize;
    };

    _list select 0;
};

_escapeString = {
    private ["_source", "_target", "_start", "_charCode"];
    _source = toArray _this;
    _start = _source find 34;
    if (_start > 0) then {
        _target = +_source;
        _target resize _start;
        for "_i" from _start to count _source - 1 do {
            _charCode = _source select _i;
            __push(_target, _charCode);
            if (_charCode == 34) then {
                __push(_target, _charCode);
            };
        };
        str toString _target;
    } else {
        str _this;
    };
};

_collectInheritedProperties = {
    private [
        "_config", "_properties", "_propertyNamesLC",
        "_property", "_propertyLC"
    ];
    _config = _this;
    _properties = [];
    _propertyNamesLC = [];
    while {
        for "_i" from 0 to count _config - 1 do {
            _property = _config select _i;
            _propertyName = toLower configName _property;
            if !(_propertyName in _propertyNamesLC) then {
                __push(_properties, _property);
                __push(_propertyNamesLC, _propertyName);
            };
        };
        configName _config != "";
    } do {
        _config = inheritsFrom _config;
    };
    _properties;
};

_dumpConfigTree = {
    private [
        "_includeInheritedProperties", "_specifyParentClass", "_result",
        "_indents", "_depth", "_pushLine", "_traverse", "_traverseArray"
    ];
    _includeInheritedProperties = argOr(1, false);
    _specifyParentClass = argOr(2, !_includeInheritedProperties);

    _result = [];
    _indents = [""];
    _depth = 0;
    _pushLine = {
        if (_depth >= count _indents) then {
            _indents set [_depth, (_indents select _depth-1) + "    "];
        };
        __push(_result, (_indents select _depth) + _this);
    };

    _traverse = {
        private "_confName";
        _confName = configName _this;
        if (isText _this) exitwith {
            _confName + " = " + (getText _this call _escapeString) + ";" call _pushLine;
        };
        if (isNumber _this) exitwith {
            _confName + " = " + str getNumber _this + ";" call _pushLine;
        };
        if (isArray _this) exitwith {
            _confName + "[] = " + (getArray _this call _traverseArray) + ";" call _pushLine;
        };
        if (isClass _this) exitwith {
            "class " + _confName + (
                configName inheritsFrom _this call {
                    if (_this == "" || !_specifyParentClass) then { "" } else { " : " + _this }
                }
            ) + " {" call _pushLine;
            if (_includeInheritedProperties) then {
                _this = _this call _collectInheritedProperties;
            };
            _depth = _depth + 1;
            for "_i" from 0 to count _this - 1 do {
                _this select _i call _traverse
            };
            _depth = _depth - 1;
            "};" call _pushLine;
        };
    };

    _traverseArray = {
        if (typeName _this == "array") exitwith {
            private "_array";
            _array = [];
            for "_i" from 0 to count _this - 1 do {
                __push(_array, _this select _i call _traverseArray);
            };
            "{" + ([_array, ", "] call _joinString) + "}";
        };
        if (typeName _this == "string") exitwith {
            _this call _escapeString;
        };
        str _this;
    };

    if (typeName arg(0) == "ARRAY") then {
        {
            _x call _traverse;
        } foreach arg(0);
    } else {
        arg(0) call _traverse;
    };

    [_result, toString [0x0D,0x0A]] call _joinString;
};

private ["_res", "_startTime", "_endTime"];
_startTime = diag_tickTime;
_res = _this call _dumpConfigTree;
_endTime = diag_tickTime;
copyToClipboard _res;
hint format["Ready\nNow get config from clipboard\ntime: %1", _endTime - _startTime];
//_res;

